package com.ecode.ecny.service.sign;

import com.ecode.ecny.exception.TrxException;
import com.ecode.ecny.service.AcctService;
import com.ecode.ecny.service.GenerateService;
import com.ecode.ecny.service.TrxFlowService;
import com.ecode.ecny.base.AppContext;
import com.ecode.ecny.base.RspHead;
import com.ecode.ecny.base.TxnStatus;
import com.ecode.ecny.dao.sign.SignInfoDtlMapper;
import com.ecode.ecny.dao.sign.SignInfoMapper;
import com.ecode.ecny.dto.BaseReqDTO;
import com.ecode.ecny.dto.sign.SignReqDTO;
import com.ecode.ecny.dto.sign.SignRspDTO;
import com.ecode.ecny.exception.TrxErrorEnums;
import com.ecode.ecny.flow.FlowChain;
import com.ecode.ecny.flow.FlowContent;
import com.ecode.ecny.mobel.sign.SignInfoDo;
import com.ecode.ecny.mobel.sign.SignInfoDtlDO;
import com.ecode.ecny.uitl.DateTimeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @ClassName ConvertInService
 * @Author mjlft
 * @Date 2021/8/14 16:23
 * @Version 1.0
 * @Description 绑卡签约
 */
@Service
public class SignService implements TrxFlowService {
    Logger logger = LoggerFactory.getLogger(SignService.class);
    public final static String SIGN_FLOW = "signService";

    //签约信息流水
    private final static String TMP_KEY_SIGN_INFO_DTL = "SIGN_INFO_DTL";
    //签约信息key
    private final static String TMP_KEY_SIGN_INFO_DO = "SIGN_INFO_DO";

    @Autowired
    private SignInfoMapper signInfoMapper;
    @Autowired
    private SignInfoDtlMapper signInfoDtlMapper;
    @Autowired
    private AcctService acctService;
    @Autowired
    private GenerateService generateService;
    @Autowired
    private SignHelpService signHelpService;

    @Override
    public void doFlow(FlowContent content) {
        //初始化流程任务
        FlowChain flowChain = FlowChain.init(this::init, "绑卡初始化");

        //添加流程
        flowChain.addNext(this::check, "绑卡校验")
                .addNext(this::process, "绑卡处理")
                .addNext(this::response, "绑卡结果生成");

        //开始执行流程
        flowChain.doFlow(content, this::errorHandler);
    }

    void init(FlowContent flowContent) {
        logger.info("【签约】初始化");
        BaseReqDTO signReqDTO = flowContent.getReqDTO();
        SignInfoDtlDO signInfoDtlDO = buildFromReqDTO(signReqDTO);
        try {
            int res = signInfoDtlMapper.save(signInfoDtlDO);
            if (res != 1) {
                throw new TrxException(TrxErrorEnums.SYS_SIGN_DTL_ERROR, TxnStatus.FAIL);
            }
        } catch (TrxException ex) {
            logger.info("【签约】初始化失败", ex);
            throw ex;
        } catch (Exception e) {
            logger.info("【签约】初始化失败", e);
            throw new TrxException(TrxErrorEnums.SYS_SIGN_DTL_ERROR, TxnStatus.FAIL);
        }
        flowContent.putKey(TMP_KEY_SIGN_INFO_DTL, signInfoDtlDO);
    }

    /**
     * 构建请求流水
     *
     * @param baseReqDTO
     * @return
     */
    private SignInfoDtlDO buildFromReqDTO(BaseReqDTO baseReqDTO) {
        SignReqDTO signReqDTO = (SignReqDTO) baseReqDTO.getBody();
        SignInfoDtlDO signInfoDtlDO = new SignInfoDtlDO();
        signInfoDtlDO.setSeqNo(baseReqDTO.getHead().getReqNo());
        signInfoDtlDO.setReqSys(baseReqDTO.getHead().getReqSysNo());
        signInfoDtlDO.setTrxStatus(TxnStatus.INIT);
        signInfoDtlDO.setOprType(AppContext.SIGN_OPR_TYPE_S);
        signInfoDtlDO.setWalletId(signReqDTO.getWalletId());
        signInfoDtlDO.setWalletName(signReqDTO.getWalletName());
        signInfoDtlDO.setAcctNo(signReqDTO.getAcctNo());
        signInfoDtlDO.setIdNo(signReqDTO.getIdNo());
        signInfoDtlDO.setIdType(signReqDTO.getIdType());
        signInfoDtlDO.setIdName(signReqDTO.getIdName());
        signInfoDtlDO.setCreateDate(DateTimeUtil.getCurrentDate());
        signInfoDtlDO.setCreateTime(DateTimeUtil.getCurrentTime());
        signInfoDtlDO.setLastUpDate(DateTimeUtil.getCurrentDate());
        signInfoDtlDO.setLastUpTime(DateTimeUtil.getCurrentTime());

        return signInfoDtlDO;
    }

    /**
     * 任务校验
     *
     * @param content
     */
    private void check(FlowContent content) {
        SignInfoDtlDO signInfoDtlDO = (SignInfoDtlDO) content.getValue(TMP_KEY_SIGN_INFO_DTL);
        logger.info("【签约】任务校验");
        //账户校验
        acctService.checkAcct(signInfoDtlDO.getIdNo(), signInfoDtlDO.getIdName(), signInfoDtlDO.getIdType(), signInfoDtlDO.getAcctNo());
        //判断是否已经签约
        SignInfoDo signInfoDo = signInfoMapper.getByAcct(signInfoDtlDO.getWalletId(), signInfoDtlDO.getAcctNo());

        content.putKey(TMP_KEY_SIGN_INFO_DO, signInfoDo);
    }

    /**
     * 任务处理
     *
     * @param content
     */
    private void process(FlowContent content) {
        logger.info("【签约】交易处理");
        SignInfoDo signInfoDo = (SignInfoDo) content.getValue(TMP_KEY_SIGN_INFO_DO);
        SignInfoDtlDO signInfoDtlDO = (SignInfoDtlDO) content.getValue(TMP_KEY_SIGN_INFO_DTL);
        boolean insert = false;
        if (signInfoDo == null) {
            signInfoDo = buildSign(signInfoDtlDO);
            insert = true;
        }

        signInfoDo.setSignStatus(AppContext.SIGN_STATUS_N);
        signInfoDtlDO.setRetCode(AppContext.RET_SUCC_CODE);
        signInfoDtlDO.setRetMsg(AppContext.RET_SUCC_MSG);

        signHelpService.signInfo(insert, signInfoDo, signInfoDtlDO);
    }

    private SignInfoDo buildSign(SignInfoDtlDO signInfoDtlDO) {
        SignInfoDo signInfoDo = new SignInfoDo();
        signInfoDo.setSignNo(generateService.generateSingNo(signInfoDtlDO.getReqSys()));
        signInfoDo.setWalletId(signInfoDtlDO.getWalletId());
        signInfoDo.setWalletName(signInfoDtlDO.getWalletName());
        signInfoDo.setAcctNo(signInfoDtlDO.getAcctNo());
        signInfoDo.setIdNo(signInfoDtlDO.getIdNo());
        signInfoDo.setIdType(signInfoDtlDO.getIdType());
        signInfoDo.setIdName(signInfoDtlDO.getIdName());
        signInfoDo.setSignStatus(AppContext.SIGN_STATUS_N);
        signInfoDo.setCreateDate(DateTimeUtil.getCurrentDate());
        signInfoDo.setCreateTime(DateTimeUtil.getCurrentTime());
        signInfoDo.setLastUpDate(DateTimeUtil.getCurrentDate());
        signInfoDo.setLastUpTime(DateTimeUtil.getCurrentTime());

        signInfoDtlDO.setSignNo(signInfoDo.getSignNo());
        return signInfoDo;
    }

    /**
     * 响应寄送
     *
     * @param content
     */
    private void response(FlowContent content) {
        createPackage(content, TxnStatus.SUCC, null);
    }

    /**
     * 错误处理
     *
     * @param content
     */
    private void errorHandler(FlowContent content, Exception ex) {
        logger.info("【签约】异常处理", ex);
        SignInfoDtlDO signInfoDtlDO = (SignInfoDtlDO) content.getValue(TMP_KEY_SIGN_INFO_DTL);
        try{
            if(ex instanceof TrxException){
                signInfoDtlDO.setRetMsg(((TrxException) ex).getTrxMsg());
                signInfoDtlDO.setRetCode(((TrxException) ex).getTrxCode());
                signInfoDtlDO.setTrxStatus(((TrxException) ex).getTrxStatus());
            }else {
                signInfoDtlDO.setRetMsg(TrxErrorEnums.SYS_ERROR.getMsg());
                signInfoDtlDO.setRetCode(TrxErrorEnums.SYS_ERROR.getCode());
                signInfoDtlDO.setTrxStatus(TxnStatus.FAIL);
            }
            signInfoDtlMapper.updateSignNo(signInfoDtlDO);
        }catch (Exception e){
            logger.info("【签约】异常更新流水失败", e);
        }
        createPackage(content, signInfoDtlDO.getTrxStatus(), ex);
    }

    /**
     * 生成响应报文
     *
     * @param content
     */
    private void createPackage(FlowContent content, String status, Exception ex) {
        SignRspDTO rspDTO = null;
        if (TxnStatus.SUCC.equals(status)) {
            rspDTO = new SignRspDTO();
            SignInfoDtlDO signInfoDtlDO = (SignInfoDtlDO) content.getValue(TMP_KEY_SIGN_INFO_DTL);
            rspDTO.setWalletId(signInfoDtlDO.getWalletId());
            rspDTO.setAcctNo(signInfoDtlDO.getAcctNo());
            rspDTO.setSignNo(signInfoDtlDO.getSignNo());
            rspDTO.setSignType(AppContext.SIGN_OPR_TYPE_S);
        }

        RspHead head = RspHead.createHead(ex);
        content.instanceRsp(rspDTO, head);
    }
}
